﻿// Decompiled with JetBrains decompiler
// Type: Netick.Interpolator
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Runtime.CompilerServices;

#nullable disable
namespace Netick;

/// <summary>
/// A struct that is used to obtain interpolation data for a network variable.
/// </summary>
public struct Interpolator
{
  internal readonly Entity Entity;
  internal readonly long DataOffsetBytes;
  public readonly float Precision;
  internal readonly int VectorFloatFieldsCount;

  public unsafe Interpolator(
    Entity entity,
    int* s,
    int offsetInWords,
    float precision,
    int vectorFloatFieldsCount)
  {
    this.Entity = entity;
    this.DataOffsetBytes = this.Entity.StateOffsetBytes + ((sbyte*) (s + offsetInWords) - (sbyte*) this.Entity.StatePtr);
    this.Precision = precision;
    this.VectorFloatFieldsCount = (int) (byte) vectorFloatFieldsCount;
  }

  /// <summary>Tries to obtain interpolation data for this frame.</summary>
  /// <returns>Returns true if there was interpolation data for this frame.</returns>
  public unsafe bool GetInterpolationData<T>(
    InterpolationSource interpolationSource,
    out T from,
    out T to,
    out float alpha)
    where T : unmanaged
  {
    Interpolation interpolation = (Interpolation) null;
    if (this.Entity == null)
    {
      from = default (T);
      to = default (T);
      alpha = -1f;
      return false;
    }
    NetickEngine engine = this.Entity.Engine;
    if (engine.IsServer)
    {
      interpolation = (Interpolation) engine._localInterpolation;
    }
    else
    {
      switch (interpolationSource)
      {
        case InterpolationSource.Auto:
          interpolation = !this.Entity.IsPredicted ? (this.Entity.AdaptiveRemoteInterpolation != null ? (Interpolation) this.Entity.AdaptiveRemoteInterpolation : (Interpolation) engine._remoteInterpolation) : (Interpolation) engine._localInterpolation;
          break;
        case InterpolationSource.PredictedSnapshot:
          interpolation = (Interpolation) engine._localInterpolation;
          break;
        case InterpolationSource.RemoteSnapshot:
          interpolation = this.Entity.AdaptiveRemoteInterpolation != null ? (Interpolation) this.Entity.AdaptiveRemoteInterpolation : (Interpolation) engine._remoteInterpolation;
          break;
      }
    }
    if (!interpolation.HasSnapshots)
    {
      from = this.GetCompressedValue<T>((int*) ((IntPtr) this.Entity.Engine._sim.StateAllocator._allocator.Blocks[this.Entity.BlockIndex].Ptr + (IntPtr) this.DataOffsetBytes), this.Precision, this.VectorFloatFieldsCount);
      to = from;
      alpha = 1f;
      return false;
    }
    alpha = interpolation.Alpha;
    byte* ptr1 = (byte*) interpolation.FromSnapshot.Blocks[this.Entity.BlockIndex].Ptr;
    byte* ptr2 = (byte*) interpolation.ToSnapshot.Blocks[this.Entity.BlockIndex].Ptr;
    int* data1 = (int*) (ptr1 + this.DataOffsetBytes);
    int* data2 = (int*) (ptr2 + this.DataOffsetBytes);
    if (this.VectorFloatFieldsCount > 0)
    {
      from = this.GetCompressedValue<T>(data1, this.Precision, this.VectorFloatFieldsCount);
      to = this.GetCompressedValue<T>(data2, this.Precision, this.VectorFloatFieldsCount);
    }
    else
    {
      from = *(T*) data1;
      to = *(T*) data2;
    }
    return true;
  }

  public unsafe bool GetInterpolationDataPtr(
    InterpolationSource interpolationSource,
    out int* from,
    out int* to,
    out float alpha)
  {
    Interpolation interpolation = (Interpolation) null;
    if (this.Entity == null)
    {
      from = (int*) null;
      to = (int*) null;
      alpha = -1f;
      return false;
    }
    NetickEngine engine = this.Entity.Engine;
    if (engine.IsServer)
    {
      interpolation = (Interpolation) engine._localInterpolation;
    }
    else
    {
      switch (interpolationSource)
      {
        case InterpolationSource.Auto:
          interpolation = !this.Entity.IsPredicted ? (this.Entity.AdaptiveRemoteInterpolation != null ? (Interpolation) this.Entity.AdaptiveRemoteInterpolation : (Interpolation) engine._remoteInterpolation) : (Interpolation) engine._localInterpolation;
          break;
        case InterpolationSource.PredictedSnapshot:
          interpolation = (Interpolation) engine._localInterpolation;
          break;
        case InterpolationSource.RemoteSnapshot:
          interpolation = this.Entity.AdaptiveRemoteInterpolation != null ? (Interpolation) this.Entity.AdaptiveRemoteInterpolation : (Interpolation) engine._remoteInterpolation;
          break;
      }
    }
    if (!interpolation.HasSnapshots)
    {
      from = (int*) ((IntPtr) this.Entity.Engine._sim.StateAllocator._allocator.Blocks[this.Entity.BlockIndex].Ptr + (IntPtr) this.DataOffsetBytes);
      to = from;
      alpha = 1f;
      return false;
    }
    alpha = interpolation.Alpha;
    byte* ptr1 = (byte*) interpolation.FromSnapshot.Blocks[this.Entity.BlockIndex].Ptr;
    byte* ptr2 = (byte*) interpolation.ToSnapshot.Blocks[this.Entity.BlockIndex].Ptr;
    int* numPtr1 = (int*) (ptr1 + this.DataOffsetBytes);
    int* numPtr2 = (int*) (ptr2 + this.DataOffsetBytes);
    from = numPtr1;
    to = numPtr2;
    return true;
  }

  /// <summary>
  /// Tries to obtain interpolation data for this frame. This is the array version.
  /// </summary>
  /// <returns>Returns true if there was interpolation data for this frame.</returns>
  public unsafe bool GetInterpolationData<T>(
    InterpolationSource interpolationSource,
    int index,
    out T from,
    out T to,
    out float alpha)
    where T : unmanaged
  {
    Interpolation interpolation = (Interpolation) null;
    if (this.Entity == null)
    {
      from = default (T);
      to = default (T);
      alpha = -1f;
      return false;
    }
    NetickEngine engine = this.Entity.Engine;
    if (engine.IsServer)
    {
      interpolation = (Interpolation) engine._localInterpolation;
    }
    else
    {
      switch (interpolationSource)
      {
        case InterpolationSource.Auto:
          interpolation = !this.Entity.IsPredicted ? (this.Entity.AdaptiveRemoteInterpolation != null ? (Interpolation) this.Entity.AdaptiveRemoteInterpolation : (Interpolation) engine._remoteInterpolation) : (Interpolation) engine._localInterpolation;
          break;
        case InterpolationSource.PredictedSnapshot:
          interpolation = (Interpolation) engine._localInterpolation;
          break;
        case InterpolationSource.RemoteSnapshot:
          interpolation = this.Entity.AdaptiveRemoteInterpolation != null ? (Interpolation) this.Entity.AdaptiveRemoteInterpolation : (Interpolation) engine._remoteInterpolation;
          break;
      }
    }
    int wordSize = NetickUtils.GetWordSize(sizeof (T));
    if (!interpolation.HasSnapshots)
    {
      int* numPtr = (int*) ((IntPtr) this.Entity.Engine._sim.StateAllocator._allocator.Blocks[this.Entity.BlockIndex].Ptr + (IntPtr) this.DataOffsetBytes);
      from = ((T*) numPtr)[wordSize * index];
      to = from;
      alpha = 1f;
      return false;
    }
    alpha = interpolation.Alpha;
    byte* ptr1 = (byte*) interpolation.FromSnapshot.Blocks[this.Entity.BlockIndex].Ptr;
    byte* ptr2 = (byte*) interpolation.ToSnapshot.Blocks[this.Entity.BlockIndex].Ptr;
    int* numPtr1 = (int*) (ptr1 + this.DataOffsetBytes);
    int* numPtr2 = (int*) (ptr2 + this.DataOffsetBytes);
    from = *(T*) (numPtr1 + wordSize * index);
    to = *(T*) (numPtr2 + wordSize * index);
    return true;
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  private unsafe T GetCompressedValue<T>(int* data, float precision, int fieldCount) where T : unmanaged
  {
    T compressedValue = default (T);
    float* numPtr = (float*) &compressedValue;
    if (fieldCount == 0)
      precision = -1f;
    if ((double) precision == -1.0)
    {
      switch (fieldCount)
      {
        case 1:
          *numPtr = *(float*) data;
          break;
        case 2:
          *numPtr = *(float*) data;
          numPtr[1] = *(float*) (data + 1);
          break;
        case 3:
          *numPtr = *(float*) data;
          numPtr[1] = *(float*) (data + 1);
          numPtr[2] = *(float*) (data + 2);
          break;
        case 4:
          *numPtr = *(float*) data;
          numPtr[1] = *(float*) (data + 1);
          numPtr[2] = *(float*) (data + 2);
          numPtr[3] = *(float*) (data + 4);
          break;
      }
    }
    else
    {
      switch (fieldCount)
      {
        case 1:
          *numPtr = (float) *data * precision;
          break;
        case 2:
          *numPtr = (float) *data * precision;
          numPtr[1] = (float) data[1] * precision;
          break;
        case 3:
          *numPtr = (float) *data * precision;
          numPtr[1] = (float) data[1] * precision;
          numPtr[2] = (float) data[2] * precision;
          break;
        case 4:
          *numPtr = (float) *data * precision;
          numPtr[1] = (float) data[1] * precision;
          numPtr[2] = (float) data[2] * precision;
          numPtr[3] = (float) data[3] * precision;
          break;
      }
    }
    return compressedValue;
  }
}
